{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b15a6cf0",
   "metadata": {},
   "outputs": [],
   "source": [
    "import logging\n",
    "\n",
    "import panel as pn\n",
    "\n",
    "pn.extension('terminal', console_output='disable')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21e1248c",
   "metadata": {},
   "source": [
    "\n",
    "The `Debugger` is an uneditable Card layout which lets your front end show logs and errors that may fire whenever your dashboard is running.\n",
    "\n",
    "When running a panel server with several end users, the debugger will let them know whenever one of their interactions did not run as planned. Information can also be logged for end users to know that everything went well in the back-end. If logger_names is not specified, events must be logged using the `panel` logger or a custom child logger, e.g. `panel.myapp`.\n",
    "\n",
    "It is also usable in a live notebook and complements the `console_output` logs.\n",
    "\n",
    "Note the debugger is based on the [terminal widget](Terminal.ipynb) and requires `pn.extension('terminal')` to be called.\n",
    "\n",
    "####  Parameters:\n",
    "\n",
    "* **``only_last``** (bool): when exceptions are logged, indicates whether only the last trace in the stack will be prompted. Note this does not change how logs are thrown to `stderr`, the logs on server side will be accessible as you programmed them. Default: `False`\n",
    "* **``level``** (int): The log level you want to be prompt on the front end. Available values are the same as in [the standard levels of the `logging` package](https://docs.python.org/3/library/logging.html#levels). Default: `logging.ERROR`\n",
    "* **``formatter_args``** (dict): arguments to pass to the [Formatter object](https://docs.python.org/3/library/logging.html#formatter-objects). You may modify the prompt format with `fmt` or date formats with `datefmt` as in the standard library. Default: `{'fmt':\"%(asctime)s [%(name)s - %(levelname)s]: %(message)s\"}`\n",
    "* **``logger_names``** (list): [loggers called with `getLogger`](https://docs.python.org/3/library/logging.html#logging.getLogger) which will be prompted to the terminal. In a server context, note only the errors thrown  within the user session will be prompted, not all errors throughout the server. Default: `['panel']`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "726fdb65",
   "metadata": {},
   "outputs": [],
   "source": [
    "debug = pn.widgets.Debugger(name='My Debugger')\n",
    "debug"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "379a097a",
   "metadata": {},
   "source": [
    "We are making a radio button attached to a callback that intentionally throws an error. Upon clicking on the error generating buttons, you will see the error number increasing in the debugger. Note the inline log was disabled with `pn.config.console_output = 'disable'` to avoid cluterring the notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f47cc25",
   "metadata": {},
   "outputs": [],
   "source": [
    "btn = pn.widgets.RadioButtonGroup(name='Throw error', value='no error', options=['ZeroDivision', 'no error', 'Custom error'], button_type='danger')\n",
    "\n",
    "def throw_error(event):\n",
    "    if event == 'ZeroDivision':\n",
    "        return pn.pane.Str(1/0)\n",
    "    elif event == 'no error':\n",
    "        return pn.pane.Str('Hello!')\n",
    "    elif event == 'Custom error':\n",
    "        raise Exception('custom error thrown')\n",
    "    \n",
    "pn.Column(btn, pn.bind(throw_error, btn))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "412df357",
   "metadata": {},
   "source": [
    "We may also send information to the front end. Let's create a new debugger with a lower logging level"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8b0473bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "logger = logging.getLogger('panel.myapp')\n",
    "\n",
    "debug_info = pn.widgets.Debugger(\n",
    "    name='Debugger info level', level=logging.INFO, sizing_mode='stretch_both',\n",
    "    logger_names=['panel.myapp'], #comment this line out to get all panel errors\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "534f883a",
   "metadata": {},
   "outputs": [],
   "source": [
    "btn_info = pn.widgets.RadioButtonGroup(name='show info', options=['debug', 'info', 'warning'])\n",
    "\n",
    "def throw_error(event):\n",
    "    msg = (event + ' sent from btn_info').capitalize()\n",
    "    if event == 'info':\n",
    "        logger.info(msg)\n",
    "    elif event == 'debug':\n",
    "        logger.debug(msg)\n",
    "    elif event == 'warning':\n",
    "        logger.warning(msg)\n",
    "    return msg\n",
    "\n",
    "c = pn.Column(btn_info, debug_info, pn.bind(throw_error, btn_info), sizing_mode='stretch_both')\n",
    "c"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "737cf3f8",
   "metadata": {},
   "source": [
    "The end user may save the logs by clicking on the floppy disk 💾 and clear the debugger by clicking on the check box button ☐."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "47bfb025",
   "metadata": {},
   "outputs": [],
   "source": [
    "debug_info.btns"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
